<template>
        <a-card title="ColorMap" class="cmapCard" :bordered="false">
            <a-collapse :default-active-key="['1']" style="margin-bottom: 0.5rem">
                <a-collapse-item header="ENSO Example" key="1">
                    <ensoExample :cList="colorList" />
                </a-collapse-item>
            </a-collapse>
            <div style="width:100%">
                <!-- 渐变 -->
                <div v-if="cmapMode=='linear'" class="bar" :style="{background: colorbar }"></div>
                <!-- 离散  -->
                <div v-else class="bar" style="display: flex;">
                    <div v-for="(c, index) in colorList" :key="index" :style="{backgroundColor: c.color}" class="cbarC" ></div>
                </div>

                <div class="ticks">
                    <div v-for="tick in ticks" :key="tick" class="tick"> </div>
                </div>
                <div class="ticks">
                    <div v-for="tick in ticks" :key="tick" class="ticklabel">{{tick}}</div>
                </div>
                
                <div style="display: flex; align-items: center; padding: 0.3rem 0.5rem;">
                    <div>颜色位置：</div>
                    <a-radio-group type="button" v-model="evenMode"
                        :disabled="cmapMode=='discrete'">
                        <a-radio :value="true">均分</a-radio>
                        <a-radio :value="false">自定</a-radio>
                    </a-radio-group>

                    <div style="width: 2rem;"/>

                    <div>过渡模式：</div>
                    <a-radio-group type="button" v-model="cmapMode">
                        <a-radio value="linear">渐变</a-radio>
                        <a-radio value="discrete">离散</a-radio>
                    </a-radio-group>
                    <div style="flex-grow: 1;"></div>
                    <a-tooltip content="可以拖动颜色到此添加">
                        <a-button type="primary" @click="addColor('#888888')" @drop="dropAddColor" @dragover="dragOver">
                            <Icon><Add12Filled /></Icon> 添加颜色
                        </a-button>
                    </a-tooltip>

                </div>

                <div style="display: flex; align-items: center; padding: 0.5rem 0.5rem; justify-content: center;">

                    <a-button @click="undo" type="text" status="warning" :disabled="historyColorList.length == 0">
                        <Icon size="1.2rem"><ArrowUndo16Filled/></Icon> 撤回
                    </a-button>
                    <a-divider direction="vertical" />
                    <a-button @click="showPresetModal=true" type="text">
                        <Icon size="1.2rem"><Color20Filled/></Icon>
                        使用预设
                    </a-button>
                    <a-button @click="showImportModal=true"  type="text">
                        <Icon size="1.2rem"><ClipboardCode16Filled/></Icon>
                        导入颜色码
                    </a-button>
                    <a-button @click="showParseImgModal=true"  type="text">
                        <Icon size="1.2rem"><Screenshot20Regular/></Icon>
                        截图识别
                    </a-button>
                    <a-divider direction="vertical" />
                    <a-button @click="showResampleModal=true"  type="text">
                        <Icon size="1.2rem"><ColorFill20Filled/></Icon>
                        重采样
                    </a-button>
                    <a-divider direction="vertical" />
                    <a-button type="text" @click="reverse">
                        <Icon size="1.2rem"><ArrowSync12Filled/></Icon>
                        反转顺序
                    </a-button>

                </div>

                <VueDraggable ref="el" v-model="colorList" class="barBox" @end="evenColorLoc">
                    <div v-for="(item, key) in colorList" :key="item.id" class="barColor">
                        <a-color-picker  v-model="colorList[key].color" showPreset :disabled-alpha="true" @popup-visible-change="changeColor">
                            <div class="color-block" :style="{backgroundColor: item.color}" > </div>
                        </a-color-picker>
                        <div class="colorValue">
                            {{ item.color }}
                        </div>
                        <a-slider :default-value="0" :style="{ width: '400px' }" v-model="item.loc"
                        :min="0" :max="100" :step="1" show-input :disabled="evenMode" />

                        <div style="display: flex; align-items: center; margin-left:0.5rem; color: rgb(var(--warning-6));"
                            v-if="!evenMode && key > 0 && item.loc <= colorList[key-1].loc"
                        >
                            <Icon size="1.5rem" ><Warning12Filled/></Icon>
                            位置需单调递增
                        </div>

                        <div style="flex-grow: 1;"/>

                        <a-popconfirm content="确定要删除这个颜色吗？" type="warning" @ok="deleteColor(item.id)">
                            <a-button type="text" status="warning" :disabled="colorList.length <= 2">
                                <template #icon>
                                    <Icon size="1.3rem" ><Delete16Filled /></Icon>
                                </template>
                            </a-button>
                        </a-popconfirm>
                    </div>
                </VueDraggable>

                <!-- 导出 -->
                <a-divider orientation="center">导出 ColorMap</a-divider>
                <div style="display: flex; align-items: center; padding: 0.3rem 0.5rem;">
                    格式：
                    <a-radio-group type="button" v-model="exportLang">
                        <a-radio value="matplotlib">Matplotlib</a-radio>
                        <a-radio value="ncl">NCL</a-radio>
                        <a-radio value="text">数组文本</a-radio>
                    </a-radio-group>

                    <div style="flex-grow: 1;"/>
                    颜色编码：
                    <a-radio-group type="button" v-model="exportColorMode" :disabled="exportLang!='text'">
                        <a-radio value="hex">HEX</a-radio>
                        <a-radio value="rgb1">0~1 RGB</a-radio>
                        <a-radio value="rgb2">0~255 RGB</a-radio>
                        <a-radio value="rgb3">rgb(0~1)</a-radio>
                        <a-radio value="hsv">HSV</a-radio>
                    </a-radio-group>
                    <div style="flex-grow: 1;"/>
                    <a-button type="primary" @click="copy(exportCode)" >复制代码</a-button>
                </div>

                <div v-if="exportLang=='ncl'" style="padding: 1rem;">
                    NCL 使用方法请参考 <a href="https://www.ncl.ucar.edu/Document/Graphics/Interfaces/gsn_define_colormap.shtml" target="_blank">NCL官方文档</a>
                </div>

                <div class="code">
                    <pre>{{exportCode}}</pre>
                </div>
            </div>
        </a-card>

    <a-drawer
        :width="700"
        v-model:visible="showPresetModal"
        placement="left"
        unmountOnClose
        :footer="false"
    >
        <template #title>
        导入预设
        </template>
        <cmapPreset @use="usePreset" />
    </a-drawer>


    <a-modal v-model:visible="showImportModal" @ok="parseCmap">
        <template #title>
          导入 ColorMap
        </template>
        <a-textarea v-model="importText" style="width: 100%; "/>
        <div>
            在此粘贴颜色数组，支持解析以下格式
            <ul>
                <li>"#0000ff", "#ff0000", ...</li>
                <li>(0, 0, 255), (255, 0, 0), ...</li>
                <li>(0.5, 0.5, 1), (1, 0.1, 0.3), ...</li>
            </ul>
        </div>
    </a-modal>


    <a-modal v-model:visible="showResampleModal" @ok="resample">
        <template #title> 重采样Colormap </template>
        <div>
            对当前 Colormap 重新采样，如果采样数量比当前少，结果将可能会不同。
        </div>
        <div style="display: flex;">

        <span>重采样数量：</span>
            <a-input-number v-model="resampleNumber" style="width: 130px; " :min="2" mode="button" />
        </div>
    </a-modal>

    <a-modal v-model:visible="showParseImgModal" :footer="false"  width="800px">
        <template #title> 截图识别 </template>
        <parseImg  @use="useParse" />
    </a-modal>
</template>

<script setup lang="ts">
import { ref, watch, computed, nextTick } from 'vue';
import { VueDraggable } from 'vue-draggable-plus'
import {Add12Filled, Delete16Filled, Warning12Filled, ArrowUndo16Filled, ArrowSync12Filled, Color20Filled, ClipboardCode16Filled, Screenshot20Regular, ColorFill20Filled} from '@vicons/fluent';
import {Icon} from '@vicons/utils';
import { hex2grb1, hex2rgb2, hex2hsv, copy, rgb2hex, rgb12hex, linear_interp_color } from '../api';
import { Notification } from '@arco-design/web-vue';
import ensoExample from './ensoExample.vue';
import cmapPreset from './cmapPreset.vue';
import parseImg from './parseImg.vue';


interface IColor {
    id: string;
    color: string;
    loc: number;
}

// 记录id
let id = 0;

const colorList = ref<IColor[]>([ ]);

const addColor = (color: string) => {
    colorList.value.push(
        {
            id: (id++).toString(),
            color: color,
            loc: 0
        }
    );
    nextTick(() => {
        evenColorLoc();
    });
}

const evenColorLoc = () => {
    const len = colorList.value.length;
    if(len > 1){
        const step = 100 / (len - 1);
        colorList.value.forEach((item, index) => {
            item.loc = index * step;
        });
    }
}

const deleteColor = (id: string) => {
    const index = colorList.value.findIndex(item => item.id === id);
    if(index >= 0){
        colorList.value.splice(index, 1);
        evenColorLoc();
    }
}

addColor("#ff0000");
addColor("#ffffff");
addColor("#0000ff");


// 生成渐变
const colorbar = computed(() => {
    return `linear-gradient(to right, ${colorList.value.map(item => {
        return `${item.color} ${item.loc}%`
    }).join(",")})`;
});

const ticks = ref<number[]>([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
const evenMode = ref(true);
const cmapMode = ref('linear'); // linear or discrete


// 拖动添加颜色
const dropAddColor = (e: DragEvent) => {
    const color = e.dataTransfer?.getData('color');
    if(color){
        addColor(color);
    }
}
const dragOver = (e: DragEvent) => {
    e.preventDefault();
}

// 交换位置后，重新排序
const dragEnd = () => {
    if (evenMode.value) {
        evenColorLoc();
    }
}

// 导出模式
const exportColorMode = ref('hex');
const exportLang = ref('matplotlib'); // matplotlib, matlab, ncl


const exportCode = computed(()=>{
    // 处理颜色


    if (exportLang.value == 'matplotlib'){
        const c = colorList.value.map(item => {
            return "'"+item.color+"'";
        });
        if (cmapMode.value == 'linear'){
            if (evenMode.value){
                return `from matplotlib.colors import LinearSegmentedColormap
myCmap = LinearSegmentedColormap.from_list("", [${c.join(',')}])`;
            }
            else{
                const loc = colorList.value.map(item => `(${(item.loc/100).toFixed(2)}, ${item.color})`);
                return `from matplotlib.colors import LinearSegmentedColormap
myCmap = LinearSegmentedColormap.from_list("", [${loc.join(',')}])`;
            }
        }
        else{
            return `from matplotlib.colors import ListedColormap
myCmap = ListedColormap([${c.join(',')}])`
        }
    }
    else if (exportLang.value == 'ncl'){
        const c = colorList.value.map(item => {
            let rgb = hex2grb1(item.color);
            rgb = rgb.slice(1, -1);
            return `"(/${rgb}/)"`;
        });

        return `cmap = (/${c.join(", ")}/)`
    }
    else if (exportLang.value == 'text'){
        if (exportColorMode.value == 'hex'){
            return colorList.value.map(item => {
                return "'" + item.color + "'";
            }).join(', ');
        }
        else if (exportColorMode.value == 'rgb1'){
            return colorList.value.map(item => {
                return hex2grb1(item.color)
            }).join(', ');
        }
        else if (exportColorMode.value == 'rgb2'){
            return colorList.value.map(item => {
                return hex2rgb2(item.color);
            }).join(', ');
        }
        else if (exportColorMode.value == 'rgb3'){
            return colorList.value.map(item => {
                return hex2grb1(item.color);
            }).join(', ');
        }
        else if (exportColorMode.value == 'hsv'){
            return colorList.value.map(item => {
                return hex2hsv(item.color);
            }).join(', ');
        }
        else{
            return ""
        }
    }
})


// ==================== 导入 ====================
const showImportModal = ref(false);
const importText = ref('');
const historyColorList = ref<IColor[][]>([]);
const historyCache = ref<IColor[]>([]);

const addHistory = () => {
    historyColorList.value.push(colorList.value);
    // 如果历史超过50个，删除最早的
    if (historyColorList.value.length > 50){
        historyColorList.value.shift();
    }
}

const changeColor = (visiable: boolean) => {
    // 打开调色板，缓存当前颜色
    if (visiable){
        historyCache.value = colorList.value.map(item => {
            return {
                id: item.id,
                color: item.color,
                loc: item.loc
            }
        });
    }
    // 关闭调色板，如果颜色改变，添加历史
    else{
        if (historyCache.value.length > 0){
            let changed = false;
            for(let i = 0; i < colorList.value.length; i++){
                if (colorList.value[i].color != historyCache.value[i].color){
                    changed = true;
                    break;
                }
            }
            if (changed){
                historyColorList.value.push(historyCache.value);
            }
        }
    }
}


const parseCmap = () => {
    const text = importText.value;
    // hex
    const c = text.match(/#([0-9a-fA-F]{6})/g);
    const c3 = text.match(/\(\s*(\d+\.\d+)\s*,\s*(\d+\.\d+)\s*,\s*(\d+\.\d+\s*)\)/g);
    const c2 = text.match(/\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g);
    if(c){
        addHistory();
        colorList.value = [];
        c.forEach(item => {
            addColor(item);
        });
    }
    // rgb (x, x, x), (x, x, x), ...，这里需要区分是0~1还是0~255
    else if(c2){
        addHistory();
        colorList.value = [];
        c2.forEach(item => {
            const rgb = item.match(/\d+/g);
            if (rgb && rgb.length === 3) {
            addColor(rgb2hex(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2])));
            }
        });
    }
    // rgb (x.x, x.x, x.x), (x.x, x.x, x.x), ...
    else if(c3){
        addHistory();
        colorList.value = [];
        c3.forEach(item => {
            const rgb = item.match(/\d+\.\d+/g);
            if (rgb && rgb.length === 3) {
            addColor(rgb12hex(parseFloat(rgb[0]), parseFloat(rgb[1]), parseFloat(rgb[2])));
            }
        });
    }
    else{
        Notification.error({
            title: '解析失败',
            content: '无法解析颜色数组，请检查格式'
        });
    }

    importText.value = '';
}

const undo = () => {
    if (historyColorList.value.length > 0){
        colorList.value = historyColorList.value.pop()!;
    }
}

// ==================== 预设 ====================

const showPresetModal = ref(false);

const usePreset = (c: string[]) => {
    addHistory();
    colorList.value = [];
    c.forEach(item => {
        addColor(item);
    });
    showPresetModal.value = false;
}

// ==================== 重采样 ====================

const showResampleModal = ref(false);
const resampleNumber = ref(5);

const resample = () => {
    const n = resampleNumber.value;
    if (n > 2){
        addHistory();
        const c = colorList.value;
        const res: IColor[] = [];
        for(let i = 0; i < n; i++){
            const loc = i / (n - 1) * 100;
            res.push({
                id: (id++).toString(),
                color: linear_interp_color(c, loc),
                loc: loc
            });
        }
        colorList.value = res;
    }
    else{
        Notification.error({
            title: '重采样失败',
            content: '重采样数量必须大于2'
        });
    }
    showResampleModal.value = false;
}

// ==================== 图片解析 ====================
// 通过粘贴截图来读取图片里的颜色
const showParseImgModal = ref(false);

const useParse = (c: string[]) => {
    addHistory();
    colorList.value = [];
    c.forEach(item => {
        addColor(item);
    });
    showParseImgModal.value = false;
}

// ==================== 反转 ====================
const reverse = () => {
    colorList.value = colorList.value.reverse();
    evenColorLoc();
}

</script>

<style scoped>
.bar{
    height: 60px;
    width: calc(100% - 4px);
    border: 2px solid var(--color-text-1);
}
.ticks{
    display: flex;
    justify-content: space-between;
    width: 100%;
}
.tick{
    height: 8px;
    width: 2px;
    background-color: var(--color-text-1);
}
.ticklabel{
    font-size: 14px;
    width: 30px;
    color: var(--color-text-1);
    display: flex;
    justify-content: center;
    align-items: center;
}
.barColor{
    width: calc(100% - 2rem);
    padding: 0.5rem 1rem;
    background-color: var(--color-bg-1);
    border-radius: 6px;
    display: flex;
    align-items: center;
    cursor: grab;
}
.barBox{
    padding: 0.7rem;
    background-color: var(--color-fill-2);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    border-radius: 10px;
}
.color-block{
    width: 64px;
    height: 40px;
    border-radius: 5px;
    cursor: pointer;
    border: 1px solid var(--color-border-1);
}
.colorValue{
    font-size: 16px;
    display: flex;
    align-items: center;
    padding: 0 0.5rem;
    width: 100px;
    justify-content: center ;
}
.cbarC{
    flex-grow: 1;
    height: 100%;
}
.code{
    padding: 0.5rem 1rem; 
    background-color: var(--color-fill-2); 
    border-radius: 10px;
    overflow: auto;
}
.cmapCard{
  width: 900px;
}
.exampleBox{
    min-width: 400px;
    flex-grow: 1;
}
</style>